﻿using Microsoft.Extensions.Options;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Protocol;
using Newtonsoft.Json;
using RuoVea.ExDto;
using RuoVea.ExEnum;
using RuoVea.ExLog;
using RuoVea.ExSugar;
using RuoVea.ExUtil;
using RuoVea.OmiApi.Dict.Entitys;
using RuoVea.OmiApi.Log.Entitys;
using SqlSugar;
using System.Text;
using System.Text.RegularExpressions;

namespace RuoVea.Iot.MqttClient.Server;


/// <summary>
/// MQTT 客户端服务
/// </summary>
public partial class MqttClientService
{
    MqttOptions _mqttOptions;
    private readonly ISqlSugarClient _sqlSugarClient;
    /// <summary>
    /// MQTT 客户端实例
    /// </summary>
    public static IMqttClient _mqttClient;

    #region 构造函数
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="options">MQTT 配置选项</param>
    /// <param name="sqlSugarClient"></param>
    public MqttClientService(IOptions<MqttOptions> options, ISqlSugarClient sqlSugarClient)
    {
        _mqttOptions = options.Value;
        _sqlSugarClient = sqlSugarClient;
    }
    #endregion

    #region 启动 MQTT 客户端
    /// <summary>
    /// 启动 MQTT 客户端
    /// </summary>
    public async Task MqttClientStart()
    {
        try
        {
            List<SysDictValue> dictValues = _sqlSugarClient.Queryable<SysDictType, SysDictValue>((a, b) => a.Id == b.TypeId)
                .Where((a, b) => a.Code == "mqtt" && a.IsDisable == YesOrNot.N && a.IsDelete == IsDelete.N)
                .Select((a, b) => b).ToList();
            if (dictValues != null && dictValues.Count > 0)
            {
                _mqttOptions.Server = dictValues.Find(x => x.Code == "Server")?.Value;
                _mqttOptions.Port = dictValues.Find(x => x.Code == "Port")?.Value.ToInt();
                _mqttOptions.UserName = dictValues.Find(x => x.Code == "UserName")?.Value;
                _mqttOptions.Password = dictValues.Find(x => x.Code == "Password")?.Value;
                _mqttOptions.ClientIdPrefix = dictValues.Find(x => x.Code == "ClientIdPrefix")?.Value;
                _mqttOptions.ClientId = dictValues.Find(x => x.Code == "ClientId")?.Value;
                _mqttOptions.MaxPayloadSize = dictValues.Find(x => x.Code == "MaxPayloadSize")?.Value.ToInt() ?? 256;
                _mqttOptions.TopicPrefix = dictValues.Find(x => x.Code == "TopicPrefix")?.Value;
                _mqttOptions.DefaultPublishTopic = dictValues.Find(x => x.Code == "DefaultPublishTopic")?.Value;
                _mqttOptions.DefaultSubscribeTopics = dictValues.Find(x => x.Code == "DefaultSubscribeTopics")?.Value;
                _mqttOptions.DefaultQoS = dictValues.Find(x => x.Code == "DefaultQoS")?.Value == "0" ? MqttQualityOfServiceLevel.AtMostOnce :
                    dictValues.Find(x => x.Code == "DefaultQoS")?.Value == "1" ? MqttQualityOfServiceLevel.AtLeastOnce : MqttQualityOfServiceLevel.ExactlyOnce;
                _mqttOptions.HeartbeatInterval = dictValues.Find(x => x.Code == "HeartbeatInterval")?.Value.ToInt() ?? 60;
            }

            var optionsBuilder = new MqttClientOptionsBuilder()
                .WithTcpServer(_mqttOptions.Server, _mqttOptions.Port)
                .WithCredentials(_mqttOptions.UserName, _mqttOptions.Password)
                .WithClientId(_mqttOptions.ClientId)
                .WithCleanSession()
                .WithTls(new MqttClientOptionsBuilderTlsParameters
                {
                    UseTls = false
                });

            var clientOptions = optionsBuilder.Build();
            _mqttClient = new MqttFactory().CreateMqttClient();

            _mqttClient.ConnectedAsync += _mqttClient_ConnectedAsync;
            _mqttClient.DisconnectedAsync += _mqttClient_DisconnectedAsync;
            _mqttClient.ApplicationMessageReceivedAsync += _mqttClient_ApplicationMessageReceivedAsync;

            await _mqttClient.ConnectAsync(clientOptions);
        }
        catch (Exception ex)
        {
            await SysLogExAdd("启动MQTT客户端", "MqttClientStart", $"{_mqttOptions.Json2Str()}");
        }
    }
    #endregion

    #region 处理连接断开事件
    /// <summary>
    /// 处理连接断开事件
    /// </summary>
    private async Task _mqttClient_DisconnectedAsync(MqttClientDisconnectedEventArgs arg)
    {
        Console.WriteLine($"客户端已断开与服务端的连接……");
        await SysLogOpAdd("处理连接断开事件", "MqttClientService._mqttClient_ConnectedAsync", "客户端已断开与服务端的连接.", "", YesOrNot.N, null, null, null);
        bool wait = true;
        while (wait)
        {
            Thread.Sleep(30 * 1000);
            await MqttClientStart();
            wait = false;
        }
        await Task.CompletedTask;
    }
    #endregion

    #region 处理连接成功事件
    /// <summary>
    /// 处理连接成功事件
    /// </summary>
    private async Task _mqttClient_ConnectedAsync(MqttClientConnectedEventArgs arg)
    {
        Console.WriteLine($"客户端已连接服务端.");
        await SysLogOpAdd("处理连接成功事件", "MqttClientService._mqttClient_ConnectedAsync", "客户端已连接服务端.", "", YesOrNot.Y, _mqttOptions.ClientId, null, null);
        List<string> defaultSubscribeTopics = _mqttOptions.DefaultSubscribeTopics.Split(',')
                .Select(t => $"{_mqttOptions.TopicPrefix}/{t.Trim()}")
                .Where(t => !string.IsNullOrWhiteSpace(t))
                .ToList() ?? new List<string>();

        defaultSubscribeTopics.Add($"{_mqttOptions.DefaultPublishTopic}/{_mqttOptions.ClientId}");
        // 获取所有要订阅的主题
        var topics = new MqttClientSubscribeOptions
        {
            TopicFilters = defaultSubscribeTopics
                .Select(topic => new MqttTopicFilterBuilder()
                    .WithTopic(topic)
                    .WithQualityOfServiceLevel(_mqttOptions.DefaultQoS)
                    .Build())
                .ToList()
        };
        await _mqttClient.SubscribeAsync(topics);

        await Task.CompletedTask;
    }
    #endregion

    #region 日志记录处理
    /// <summary>
    /// 
    /// </summary>
    /// <param name="name"></param>
    /// <param name="className"></param>
    /// <param name="message"></param>
    /// <param name="result"></param>
    /// <param name="success"></param>
    /// <param name="clientId"></param>
    /// <param name="topic"></param>
    /// <param name="qos"></param>
    /// <returns></returns>
    async Task SysLogOpAdd(string name, string className, string message, string result, YesOrNot success, string clientId, string topic, MqttQualityOfServiceLevel? qos)
    {
        LogFactory.Info(message);
        await _sqlSugarClient.Insertable(new SysLogOp
        {
            ClassName = className,
            Message = message,
            Param = message,
            ReqMethod = qos == null ? "mqtt" : qos + "",
            Result = result,
            Name = name,
            Success = success,
            Ip = clientId,
            Location = topic,
            CreateTime = DateTime.Now,
            OpTime = DateTime.Now
        }).ExecuteCommandAsync();
    }

    async Task SysLogExAdd(string name, string className, string message)
    {
        LogFactory.Error(message);
        await _sqlSugarClient.Insertable(new SysLogEx
        {
            ClassName = className,
            ExceptionMsg = message,
            MethodName = name,
            CreateTime = DateTime.Now,
            ExceptionTime = DateTime.Now
        }).ExecuteCommandAsync();
    }
    #endregion

    #region 处理接收到的消息
    /// <summary>
    /// 处理接收到的消息
    /// </summary>
    private async Task _mqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
    {
        string clientId = arg.ClientId;
        string topic = arg.ApplicationMessage.Topic;
        string payload = Encoding.UTF8.GetString(arg.ApplicationMessage.Payload);
        MqttQualityOfServiceLevel qos = arg.ApplicationMessage.QualityOfServiceLevel;

        await SysLogOpAdd("接收到的消息", "MqttClientService._mqttClient_ApplicationMessageReceivedAsync", $"{payload}", "", YesOrNot.Y, clientId, topic, qos);
        try
        {
            // 1. JSON格式检查
            if (!IsValidJsonFormat(payload))
            {
                await SysLogExAdd("非标准JSON格式", "IsValidJsonFormat", $"{payload}");
                await Task.CompletedTask;
            }
            if (_mqttOptions.DefaultSubscribeTopics.Split(',').Select(t => $"{_mqttOptions.TopicPrefix}/{t.Trim()}").Any(t => topic == t))//订阅的
            {
                RespMqttPayloadDto? respMqttPayloadDto = payload.DeserializeObject<RespMqttPayloadDto>();
                if (respMqttPayloadDto.Func == "clientid")
                {
                    string result = $"{_mqttOptions.ClientId}";
                    await PublishToClientAsync(respMqttPayloadDto, result, $"{respMqttPayloadDto.ClientId}");
                    await SysLogOpAdd("获取 clientid 处理消息成功", "ProcessMessage", $"{respMqttPayloadDto.Data}", result, YesOrNot.Y, clientId, topic, qos);
                }
            }
            else
            {
                //特定发布的订阅消息
                // 2. 尝试反序列化
                ReqMqttPayloadDto? mqttPayload = payload.DeserializeObject<ReqMqttPayloadDto>();
                // 3. 执行全面的Func校验
                if (!await ValidateFuncDataAsync(mqttPayload, topic, clientId, payload, qos))
                {
                    await Task.CompletedTask;
                }

                // 4. 在这里添加您的业务处理逻辑
                await ProcessMessage(clientId, topic, qos, mqttPayload);
            }
        }
        catch (JsonException ex)
        {
            await SysLogExAdd("JsonException", "_mqttClient_ApplicationMessageReceivedAsync", $"JSON解析失败：客户端ID=【{clientId}】主题=【{topic}】内容=【{payload}】QoS=【{qos}】, 错误={ex.Message}");
        }
        catch (Exception ex)
        {
            await SysLogExAdd("Exception", "_mqttClient_ApplicationMessageReceivedAsync", $"消息处理异常：客户端ID=【{clientId}】主题=【{topic}】内容=【{payload}】QoS=【{qos}】, 错误={ex.Message}");
        }

        await Task.CompletedTask;
    }

    /// <summary>
    /// 检查是否是有效的JSON格式
    /// </summary>
    /// <param name="payload"></param>
    /// <returns></returns>
    private bool IsValidJsonFormat(string payload)
    {
        // 基础JSON格式检查
        if (string.IsNullOrWhiteSpace(payload)) return false;
        if (!payload.StartsWith("{") || !payload.EndsWith("}")) return false;

        // 扩展检查：确保包含键值对
        return payload.Contains(":") && payload.Contains("\"");
    }
    /// <summary>
    /// 执行全面的Func数据校验
    /// </summary>
    private async Task<bool> ValidateFuncDataAsync(ReqMqttPayloadDto? mqttPayload, string topic, string clientId, string payload, MqttQualityOfServiceLevel qos)
    {
        // 检查1: 反序列化是否成功
        if (mqttPayload == null)
        {
            await SysLogExAdd("ValidateFuncData", "MqttClientService", $"反序列化失败：客户端ID=【{clientId}】主题=【{topic}】内容=【{payload}】QoS=【{qos}】");
            return false;
        }

        // 检查2: Func属性是否存在
        if (mqttPayload.Func == null)
        {
            await SysLogExAdd("ValidateFuncData", "MqttClientService", $"缺少 Func 属性：客户端ID=【{clientId}】主题=【{topic}】内容=【{payload}】QoS=【{qos}】");
            return false;
        }

        // 检查3: Func值是否为空或空白
        if (string.IsNullOrWhiteSpace(mqttPayload.Func.ToString()))
        {
            await SysLogExAdd("ValidateFuncData", "MqttClientService", $"Func 值为空：客户端ID=【{clientId}】主题=【{topic}】内容=【{payload}】QoS=【{qos}】");
            return false;
        }

        // 检查4: Func数据类型是否正确
        if (mqttPayload.Func is not string)
        {
            await SysLogExAdd("ValidateFuncData", "MqttClientService", $"Func 类型错误：应为字符串，实际={mqttPayload.Func.GetType().Name}，客户端ID=【{clientId}】主题=【{topic}】内容=【{payload}】QoS=【{qos}】");
            return false;
        }

        string funcValue = mqttPayload.Func.ToString()!;

        // 检查5: Func值长度限制
        if (funcValue.Length > 50)
        {
            await SysLogExAdd("ValidateFuncData", "MqttClientService", $"Func 值过长：{funcValue}，客户端ID=【{clientId}】主题=【{topic}】内容=【{payload}】QoS=【{qos}】");
            return false;
        }

        // 检查6: Func值格式校验（仅允许字母、数字和下划线）
        if (!Regex.IsMatch(funcValue, @"^[a-zA-Z0-9_]+$"))
        {
            await SysLogExAdd("ValidateFuncData", "MqttClientService", $"Func 格式无效：{funcValue}，客户端ID=【{clientId}】主题=【{topic}】内容=【{payload}】QoS=【{qos}】");
            return false;
        }

        //// 检查7: Func值是否在允许的范围内
        //var allowedFuncs = new HashSet<string>(StringComparer.OrdinalIgnoreCase) {
        //"UpdateStatus", "GetConfig", "SetParameter", "ResetDevice", "ReportData"
        //};

        //if (!allowedFuncs.Contains(funcValue))
        //{
        //    LogFactory.Info($"无效 Func 值：{funcValue}，客户端ID=【{ClientId}】主题=【{topic}】内容=【{payload}】QoS=【{qos}】");
        //    return false;
        //}

        return true;
    }
    #endregion

    #region 发布消息
    /// <summary>
    /// 发布消息（使用默认主题）
    /// </summary>
    public async Task PublishDefault(string data)
    {
        await PublishAsync(data);
    }

    /// <summary>
    /// 发布消息（可指定主题）
    /// </summary>
    /// <param name="requestId">requestId</param>
    /// <param name="func">调用的方法</param>
    /// <param name="data">数据</param>
    /// <param name="respFunc">回调方法</param>
    /// <param name="topic">主题</param>
    /// <returns></returns>
    public async Task<bool> Publish( string requestId,string func, object data, string respFunc = null, string topic = null)
    {
        if (string.IsNullOrWhiteSpace(func))
            return false;
        await PublishAsync(new RespMqttPayloadDto { Id = requestId, ClientId = _mqttOptions.ClientId, Data = data, Func = func, RespFunc = respFunc }, topic);
        return true;
    }

    /// <summary>
    /// 发布消息（可指定主题）
    /// </summary>
    /// <param name="mqttPayload"></param>
    /// <param name="topic">主题</param>
    /// <returns></returns>
    public async Task<bool> PublishAsync(RespMqttPayloadDto mqttPayload, string? topic = null)
    {
        if (mqttPayload == null)
            return false;
        if (string.IsNullOrWhiteSpace(mqttPayload.Func))
            return false;
        await PublishAsync(Serialize(mqttPayload), topic);
        return true;
    }
    private string Serialize(object payload)
    {
        return System.Text.Json.JsonSerializer.Serialize(payload, new System.Text.Json.JsonSerializerOptions { PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase });
    }
    /// <summary>
    /// 发布消息给请求方
    /// </summary>
    /// <param name="requestId"></param>
    /// <param name="func"></param>
    /// <param name="data"></param>
    /// <param name="respFunc"></param>
    /// <param name="clientId"></param>
    /// <returns></returns>
    public async Task<bool> PublishToClientAsync(string requestId, string func, object data, string respFunc, string clientId)
    {
        await PublishToClientAsync(new RespMqttPayloadDto { ClientId = _mqttOptions.ClientId, Data = data, Func = func, RespFunc = respFunc }, clientId);
        return true;
    }
    /// <summary>
    /// 发布消息给请求方
    /// </summary>
    /// <param name="mqttPayload"></param>
    /// <param name="data"></param>
    /// <param name="clientId"></param>
    /// <returns></returns>
    public async Task<bool> PublishToClientAsync(RespMqttPayloadDto mqttPayload, object data, string clientId)
    {
        mqttPayload.Data = data;
        await PublishToClientAsync(mqttPayload, clientId);
        return true;
    }
    /// <summary>
    /// 发布消息给请求方
    /// </summary>
    /// <param name="mqttPayload"></param>
    /// <param name="clientId">主题</param>
    /// <returns></returns>
    public async Task<bool> PublishToClientAsync(RespMqttPayloadDto mqttPayload, string clientId)
    {
        if (mqttPayload == null)
            return false;
        if (string.IsNullOrWhiteSpace(mqttPayload.Func))
            return false;
        var targetTopic = $"{_mqttOptions.DefaultPublishTopic}/{clientId}";
        await PublishAsync(Serialize(mqttPayload), targetTopic);
        return true;
    }
    /// <summary>
    /// 发布消息（可指定主题）
    /// </summary>
    /// <param name="data">数据</param>
    /// <param name="topic">主题</param>
    public async Task PublishAsync(string data, string? topic = null)
    {
        // 检查 payload 长度
        byte[] payloadBytes = Encoding.UTF8.GetBytes(data);

        if (payloadBytes.Length > _mqttOptions.MaxPayloadSize * 1024) // 256KB
        {
            await SysLogExAdd("Publish", "Publish", $"Payload 过大: {payloadBytes.Length} 字节");
            return;
        }

        var targetTopic = string.IsNullOrWhiteSpace(topic) ? $"{_mqttOptions.DefaultPublishTopic}/{_mqttOptions.ClientId}" : topic;
        var message = new MqttApplicationMessage
        {
            Topic = targetTopic,
            Payload = Encoding.UTF8.GetBytes(data),
            QualityOfServiceLevel = _mqttOptions.DefaultQoS,
            Retain = true
        };

        await _mqttClient.PublishAsync(message);
    }
    #endregion

    #region 业务处理逻辑
    /// <summary>
    /// 业务处理逻辑
    /// </summary>
    /// <param name=""></param>
    /// <param name="clientId"></param>
    /// <param name="topic"></param>
    /// <param name="qos"></param>
    /// <param name="mqttPayload"></param>
    /// <returns></returns>
    public async Task ProcessMessage(string clientId, string topic, MqttQualityOfServiceLevel? qos, ReqMqttPayloadDto mqttPayload)
    {
        RespMqttPayloadDto respMqttPayload = new RespMqttPayloadDto { ClientId = _mqttOptions.ClientId, Func = mqttPayload.Func, RespFunc = mqttPayload.RespFunc, Id = mqttPayload.Id };
        try
        {
            string funcName = mqttPayload.Func?.ToLower();
            #region openapi 接口调用业务处理
            if (funcName == "openapi")
            {
                var client = new ApiClient();
                var (response, headersDict) = await client.SendRequestAsync(mqttPayload);
                if (response.IsSuccessStatusCode)
                {
                    var content = await response.Content.ReadAsStringAsync();
                    //respMqttPayload.Data = new { Headers = headersDict, body = content };
                    respMqttPayload.Data = content;
                    await PublishToClientAsync(respMqttPayload, mqttPayload.ClientId);
                    await SysLogOpAdd("获取 openapi 处理消息成功", "ProcessMessage", $"{mqttPayload.Json2Str()}", respMqttPayload.Json2Str(), YesOrNot.Y, clientId, topic, qos);
                }
                else
                {
                    await RespError(clientId, topic, qos, mqttPayload, respMqttPayload, $"请求接口失败:Url=【{mqttPayload.Data.Url}】{response?.Content?.ReadAsStringAsync().Result}");
                }
            }
            #endregion
            #region 获取处理客户端业务处理
            //else if (funcName == "clientid")
            //{
            //    string result = $"{_mqttOptions.ClientId}";
            //    await Publish(result, $"{_mqttOptions.DefaultPublishTopic}/{mqttPayload.ClientId}");
            //    await SysLogOpAdd("获取 clientid 处理消息成功", "ProcessMessage", $"{mqttPayload.Data}", result, YesOrNot.Y, ClientId, topic, qos);
            //} 
            #endregion
            else
            {
                await RespError(clientId, topic, qos, mqttPayload, respMqttPayload, $"没有定义实现方法: Func={mqttPayload.Func}");
                await SysLogExAdd("业务处理逻辑", "ProcessMessage", $"没有定义实现方法: Func={mqttPayload.Func}");
            }
        }
        catch (Exception ex)
        {
            await RespError(clientId, topic, qos, mqttPayload, respMqttPayload, $"消息处理异常：订阅客户端ID=【{clientId}】主题=【{topic}】QoS=【{qos}】,发布客户端ID=【{mqttPayload.ClientId}】 错误={ex.Message}");
            await SysLogExAdd("ProcessMessage", "_mqttClient_ApplicationMessageReceivedAsync", $"消息处理异常：订阅客户端ID=【{clientId}】主题=【{topic}】QoS=【{qos}】,发布客户端ID=【{mqttPayload.ClientId}】 错误={ex.Message}");
        }
    }

    private async Task RespError(string clientId, string topic, MqttQualityOfServiceLevel? qos, ReqMqttPayloadDto mqttPayload, RespMqttPayloadDto respMqttPayload, string message)
    {
        respMqttPayload.RespFunc = "RespError";
        respMqttPayload.Data = new
        {
            body = Serialize(new RestfulResult { Code = CodeStatus.BadRequest, Message = message })
        };
        await PublishToClientAsync(respMqttPayload, mqttPayload.ClientId);
        await SysLogOpAdd("获取 openapi 处理消息失败", "ProcessMessage", $"{mqttPayload.Json2Str()}", respMqttPayload.Json2Str(), YesOrNot.N, clientId, topic, qos);
    }
    #endregion
}
